﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Drawing;

namespace Assignment_2
{
    class TheWorld : GameObject
    {
        #region Variables
        private Texture2D texture;
        private VertexPositionNormalTexture[] vertices;
        private short[] indices;
        private VertexBuffer vertexBuffer;
        private IndexBuffer indexBuffer;
        //private Microsoft.Xna.Framework.Color c;
      //  private int xVertices, yVertices, count;
        private int slices, stacks, count;
       // private int slice, stack;
        //private bool xIsEven;
        private WorldMapStuff worldMap;
        private float radius;
        private float leftRightRotation = 1.0f;
        private float rotationSpeed = 1.0f;


        public float minWaterSize { get; set; }
        #endregion

        #region Constants
        private const int MAPSIZE = 100;
        private const float RADIUS = 1;
        #endregion
        
        public TheWorld(GraphicsDevice graphicsDevice, WorldMapStuff wS,int slices, int stacks, float radius, Matrix realWorld, Texture2D texture) : base(graphicsDevice, realWorld)
        {            
            //worldMap = new WorldMapStuff();
            worldMap = wS;
            //worldMap.TextureTo2DArray(texture); 

            this.graphicsDevice = graphicsDevice;
            this.slices = slices;
            this.stacks = stacks;
            this.texture = texture;
            //checks wether the nr of x vertices is even and sets the variable accordingly
            //to get the right tile in the beginning of every row.
            //this.xIsEven = (slices % 2 == 0);
            this.radius = radius;

            InitializeVertices();
            InitializeIndices();

            vertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionNormalTexture), vertices.Length, BufferUsage.WriteOnly);
            indexBuffer = new IndexBuffer(graphicsDevice, typeof(short), indices.Length, BufferUsage.WriteOnly);
            vertexBuffer.SetData<VertexPositionNormalTexture>(vertices);
            indexBuffer.SetData<short>(indices);
            //
            minWaterSize = worldMap.minWaterSize;
        }

        /// <summary>
        /// Allows the world to run logic.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="worldMatrix"></param>
        public override void Update(GameTime gameTime, Matrix worldMatrix)
        {
            foreach (GameObject child in children)
            {
                child.Update(gameTime, worldMatrix);
            }

            objWorld = Matrix.CreateFromQuaternion(this._rotation);
        }

        /// <summary>
        /// This is called when the world should draw itself.
        /// </summary>
        /// <param name="effect">The effect to use.</param>
        /// <param name="world">The world matrix.</param>
        override public void Draw(BasicEffect effect, Matrix world) 
        {
            foreach (GameObject gameObject in children)
            {
                gameObject.Draw(effect, objWorld);
            }

            effect.World = objWorld;
            effect.EnableDefaultLighting();
            effect.CurrentTechnique.Passes[0].Apply();
            graphicsDevice.SetVertexBuffer(vertexBuffer);
            graphicsDevice.Indices = indexBuffer;
            graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Length, 0, indices.Length / 3);
        }

        public override void InitializeVertices()
        {
            this.vertices = new VertexPositionNormalTexture[(slices + 1) * (stacks + 1)];
            int index = 0;
            for (int stack = 0; stack <= stacks; stack++)
            {
                for (int slice = 0; slice <= slices; slice++)
                {
                    Vector3 planarCoords = new Vector3(slice,stack,radius);
                    Vector3 sphereCoords;
                    sphereCoords = worldMap.getSphereCoords(planarCoords);
                    //sphereCoords = this.getSphereCoords(planarCoords);
                    vertices[index++] = new VertexPositionNormalTexture(sphereCoords, 
                                                                    sphereCoords, 
                                                                    new Vector2((float)slice / (float)slices, (float)stack / (float)stacks));
                } 
            }
        }

        public override void InitializeIndices()
        {
            this.indices = new short[slices * stacks * 6];
            int index = 0;
            int k = slices + 1;
            bool flippedTile = false;

            for (int stack = 0; stack < stacks; stack++)
            {
                for (int slice = 0; slice < slices; slice++)
                {
                    if (!flippedTile)
                    {
                        indices[index++] = (short)((stack + 0) * k + slice);
                        indices[index++] = (short)((stack + 1) * k + slice);
                        indices[index++] = (short)((stack + 0) * k + slice + 1);

                        indices[index++] = (short)((stack + 0) * k + slice + 1);
                        indices[index++] = (short)((stack + 1) * k + slice);
                        indices[index++] = (short)((stack + 1) * k + slice + 1);
                        flippedTile = !flippedTile;
                    }
                    else
                    {
                        indices[index++] = (short)((stack + 0) * k + slice);
                        indices[index++] = (short)((stack + 1) * k + slice + 1);
                        indices[index++] = (short)((stack + 0) * k + slice + 1);

                        indices[index++] = (short)((stack + 0) * k + slice);
                        indices[index++] = (short)((stack + 1) * k + slice);
                        indices[index++] = (short)((stack + 1) * k + slice + 1);
                        flippedTile = !flippedTile;
                    }
                  
                }
                flippedTile = !flippedTile;
            }   
        }

        /// <summary>
        /// Allows the world sphere to rotate with a certain speed
        /// and in a certain direction.
        /// </summary>
        /// <param name="rotationSpeed">The rotational speed.</param>
        /// <param name="dir">The direction.</param>
        public void Rotate(float rotationSpeed, Constants.Direction dir)
        {
            if (dir == Constants.Direction.Forward)
            {
                this._rotation = Quaternion.Concatenate(this._rotation, Quaternion.CreateFromYawPitchRoll(0, rotationSpeed, 0));
            }
            else if (dir == Constants.Direction.Backward)
            {
                this._rotation = Quaternion.Concatenate(this._rotation, Quaternion.CreateFromYawPitchRoll(0, -rotationSpeed, 0));
            }
            else if (dir == Constants.Direction.Left)
            {
                this._rotation = Quaternion.Concatenate(this._rotation, Quaternion.CreateFromYawPitchRoll(-rotationSpeed, 0, 0));
            }
            else if (dir == Constants.Direction.Right)
            {
                this._rotation = Quaternion.Concatenate(this._rotation, Quaternion.CreateFromYawPitchRoll(rotationSpeed, 0, 0));
            }
        }

        public Vector3 getSphereCoords(Vector3 planarCoords) {
            Vector3 sphereCoords = new Vector3();
            sphereCoords.X = (planarCoords.Z + worldMap.CalculateZ(planarCoords.X / (float)slices, planarCoords.Y / (float)stacks)) * (float)Math.Cos(MathHelper.PiOver2 - planarCoords.Y * (MathHelper.Pi / stacks)) * (float)Math.Sin(planarCoords.X * MathHelper.TwoPi / slices);
                //-(planarCoords.Z + worldMap.CalculateZ(planarCoords.X / (float)slices, planarCoords.Y / (float)stacks)) * (float)Math.Cos(MathHelper.PiOver2 - planarCoords.Y * (MathHelper.Pi / stacks)) * (float)Math.Sin(planarCoords.X * MathHelper.TwoPi / slices);
            //sphereCoords.X = planarCoords.X;
            sphereCoords.Y = (planarCoords.Z + worldMap.CalculateZ(planarCoords.X / (float)slices, planarCoords.Y / (float)stacks)) * (float)Math.Sin(MathHelper.PiOver2 - planarCoords.Y * (MathHelper.Pi / stacks));
            //sphereCoords.Y = planarCoords.Y;
            sphereCoords.Z = -(planarCoords.Z + worldMap.CalculateZ(planarCoords.X / (float)slices, planarCoords.Y / (float)stacks)) * (float)Math.Cos(MathHelper.PiOver2 - planarCoords.Y * (MathHelper.Pi / stacks)) * (float)Math.Cos(planarCoords.X * MathHelper.TwoPi / slices);
            //sphereCoords.Z = worldMap.CalculateZ(planarCoords.X / (float)slices, planarCoords.Y / (float)stacks); //- (planarCoords.Z + worldMap.CalculateZ(planarCoords.X / (float)slices, planarCoords.Y / (float)stacks)) * (float)Math.Cos(MathHelper.PiOver2 - planarCoords.Y * (MathHelper.Pi / stacks)) * (float)Math.Cos(planarCoords.X * MathHelper.TwoPi / slices);
              
            return sphereCoords;
        }
    }    
}